Making rdist non-suid but still functional

nms@ns1.rutgers.edu
Mon, 28 Mar 94 16:23:47 EST

 * Copyright 1994 by Rutgers University, New Brunswick, New Jersey, USA.
 *
 * RUTGERS UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
 * EVENT SHALL RUTGERS UNIVERSITY BE LIABLE FOR ANY SPECIAL, INDIRECT OR
 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.

RDIST CHANGES:

   The attached 2 patches, which when applied to a version of rdist available
on gatekeeper.dec.com allow it to be compiled and installed under SunOS 4.1.3
without the SUID bit.  The changes also allow rdist to be used at sites where
"rhosts" has been disabled - it uses rexec to make the connection.  The use of
rsh can be disabled by using a new flag (-E).

The source that these changes are based upon can be obtained using the
following URL

ftp://gatekeeper.dec.com/.3/BSD-net1/src/ucb/rdist.tar.Z


patch-1:
-------
   This fixes some problems with the definition of "tmpfile" as a variable.
It renames it to tempfile.


patch-2:
-------
   Contains the meat of the rdist fix.

Makefile: Add the new file rconn.c to be compiled. Also add the default
         location of the "rsh" command.

defs.h main.c: Add a new flag (-E) to prevent the use of rsh.

docmd.c: A state machine is added to try making a connection in the following
	 order,
         * 1) try rsh
         * 2) try rexec with saved password (if it exists)
         * 3) try rexec and ask for new password

	 If the -E flag is given the state machine always starts at 2.
         (This option may be of use if you have a firewall)
         
rconn.c: A new file that uses the POSIX socketpair() call to set up a remote
         connection using rsh.  This file should be relatively simple to port.

         Caveats:
         a) It doesn't reap children, a signal handler could be added.
         b) You will need to change the ordering of the exec arguments if your
            rsh command only accepts the -l flag after the hostname.

=== patch-1 ==================================================================

*** defs.h.~1~	Wed Jun 29 23:19:44 1988
--- defs.h	Thu Mar 17 17:15:50 1994
***************
*** 123,129 ****
  extern int nerrs;		/* number of errors seen */
  extern int rem;			/* remote file descriptor */
  extern int iamremote;		/* acting as remote server */
! extern char tmpfile[];		/* file name for logging changes */
  extern struct linkbuf *ihead;	/* list of files with more than one link */
  extern struct passwd *pw;	/* pointer to static area used by getpwent */
  extern struct group *gr;	/* pointer to static area used by getgrent */
--- 123,129 ----
  extern int nerrs;		/* number of errors seen */
  extern int rem;			/* remote file descriptor */
  extern int iamremote;		/* acting as remote server */
! extern char tempfile[];		/* file name for logging changes */
  extern struct linkbuf *ihead;	/* list of files with more than one link */
  extern struct passwd *pw;	/* pointer to static area used by getpwent */
  extern struct group *gr;	/* pointer to static area used by getgrent */
*** docmd.c.~1~	Wed Jun 29 23:19:45 1988
--- docmd.c	Thu Mar 17 17:16:21 1994
***************
*** 121,128 ****
  		signal(SIGPIPE, lostconn);
  		if (!makeconn(rhost))
  			return;
! 		if ((lfp = fopen(tmpfile, "w")) == NULL) {
! 			fatal("cannot open %s\n", tmpfile);
  			exit(1);
  		}
  	}
--- 121,128 ----
  		signal(SIGPIPE, lostconn);
  		if (!makeconn(rhost))
  			return;
! 		if ((lfp = fopen(tempfile, "w")) == NULL) {
! 			fatal("cannot open %s\n", tempfile);
  			exit(1);
  		}
  	}
***************
*** 156,164 ****
  	}
  	for (sc = cmds; sc != NULL; sc = sc->sc_next)
  		if (sc->sc_type == NOTIFY)
! 			notify(tmpfile, rhost, sc->sc_args, 0);
  	if (!nflag) {
! 		(void) unlink(tmpfile);
  		for (; ihead != NULL; ihead = ihead->nextp) {
  			free(ihead);
  			if ((opts & IGNLNKS) || ihead->count == 0)
--- 156,164 ----
  	}
  	for (sc = cmds; sc != NULL; sc = sc->sc_next)
  		if (sc->sc_type == NOTIFY)
! 			notify(tempfile, rhost, sc->sc_args, 0);
  	if (!nflag) {
! 		(void) unlink(tempfile);
  		for (; ihead != NULL; ihead = ihead->nextp) {
  			free(ihead);
  			if ((opts & IGNLNKS) || ihead->count == 0)
***************
*** 333,339 ****
  	if (nflag || (options & VERIFY))
  		tfp = NULL;
  	else {
! 		if ((tfp = fopen(tmpfile, "w")) == NULL) {
  			error("%s: %s\n", stamp, sys_errlist[errno]);
  			return;
  		}
--- 333,339 ----
  	if (nflag || (options & VERIFY))
  		tfp = NULL;
  	else {
! 		if ((tfp = fopen(tempfile, "w")) == NULL) {
  			error("%s: %s\n", stamp, sys_errlist[errno]);
  			return;
  		}
***************
*** 358,366 ****
  		(void) fclose(tfp);
  	for (sc = cmds; sc != NULL; sc = sc->sc_next)
  		if (sc->sc_type == NOTIFY)
! 			notify(tmpfile, NULL, sc->sc_args, lastmod);
  	if (!nflag && !(options & VERIFY))
! 		(void) unlink(tmpfile);
  }
  
  /*
--- 358,366 ----
  		(void) fclose(tfp);
  	for (sc = cmds; sc != NULL; sc = sc->sc_next)
  		if (sc->sc_type == NOTIFY)
! 			notify(tempfile, NULL, sc->sc_args, lastmod);
  	if (!nflag && !(options & VERIFY))
! 		(void) unlink(tempfile);
  }
  
  /*
*** main.c.~1~	Wed Jun 29 23:19:47 1988
--- main.c	Thu Mar 17 17:16:21 1994
***************
*** 34,41 ****
   */
  
  char	*distfile = NULL;
! char	tmpfile[] = "/tmp/rdistXXXXXX";
! char	*tmpname = &tmpfile[5];
  
  int	debug;		/* debugging flag */
  int	nflag;		/* NOP flag, just print commands without executing */
--- 34,41 ----
   */
  
  char	*distfile = NULL;
! char	tempfile[] = "/tmp/rdistXXXXXX";
! char	*tmpname = &tempfile[5];
  
  int	debug;		/* debugging flag */
  int	nflag;		/* NOP flag, just print commands without executing */
***************
*** 163,169 ****
  	*hp = NULL;
  
  	setreuid(0, userid);
! 	mktemp(tmpfile);
  
  	if (iamremote) {
  		server();
--- 163,169 ----
  	*hp = NULL;
  
  	setreuid(0, userid);
! 	mktemp(tempfile);
  
  	if (iamremote) {
  		server();
*** server.c.~1~	Wed Jun 29 23:19:48 1988
--- server.c	Thu Mar 17 17:16:18 1994
***************
*** 1451,1457 ****
   */
  cleanup()
  {
! 	(void) unlink(tmpfile);
  	exit(1);
  }
  
--- 1451,1457 ----
   */
  cleanup()
  {
! 	(void) unlink(tempfile);
  	exit(1);
  }
  
=== patch-2 ==================================================================

*** Makefile.orig	Tue Sep 20 20:18:13 1988
--- Makefile	Fri Feb  4 13:37:38 1994
***************
*** 18,26 ****
  #
  LIBC=	/lib/libc.a
  RDIST=	/usr/ucb/rdist
! CFLAGS=	-O -DRDIST=\"${RDIST}\"
! SRCS=	docmd.c expand.c gram.y lookup.c main.c server.c
! OBJS=	docmd.o expand.o gram.o lookup.o main.o server.o
  MAN=	rdist.0
  
  all: rdist
--- 18,27 ----
  #
  LIBC=	/lib/libc.a
  RDIST=	/usr/ucb/rdist
! RSH= /usr/ucb/rsh
! CFLAGS=	-O -DRDIST=\"${RDIST}\" -DRSH=\"${RSH}\"
! SRCS=	docmd.c expand.c gram.y lookup.c main.c server.c rconn.c
! OBJS=	docmd.o expand.o gram.o lookup.o main.o server.o rconn.o
  MAN=	rdist.0
  
  all: rdist
*** defs.h.orig	Wed Jun 29 23:19:44 1988
--- defs.h	Fri Feb  4 17:17:31 1994
***************
*** 118,123 ****
--- 118,124 ----
  extern int debug;		/* debugging flag */
  extern int nflag;		/* NOP flag, don't execute commands */
  extern int qflag;		/* Quiet. don't print messages */
+ extern int norshflag;           /* Don't use rsh - always use rexec */
  extern int options;		/* global options */
  
  extern int nerrs;		/* number of errors seen */
*** docmd.c.orig	Wed Jun 29 23:19:45 1988
--- docmd.c	Fri Feb  4 17:22:00 1994
***************
*** 178,185 ****
  	register char *ruser, *cp;
  	static char *cur_host = NULL;
  	static int port = -1;
! 	char tuser[20];
! 	int n;
  	extern char user[];
  	extern int userid;
  
--- 178,185 ----
  	register char *ruser, *cp;
  	static char *cur_host = NULL;
  	static int port = -1;
! 	char tuser[20], *mode = NULL;
! 	int n, state;
  	extern char user[];
  	extern int userid;
  
***************
*** 209,254 ****
  		ruser = user;
  	if (!qflag)
  		printf("updating host %s\n", rhost);
- 	(void) sprintf(buf, "%s -Server%s", RDIST, qflag ? " -q" : "");
- 	if (port < 0) {
- 		struct servent *sp;
- 
- 		if ((sp = getservbyname("shell", "tcp")) == NULL)
- 			fatal("shell/tcp: unknown service");
- 		port = sp->s_port;
- 	}
  
! 	if (debug) {
! 		printf("port = %d, luser = %s, ruser = %s\n", ntohs(port), user, ruser);
! 		printf("buf = %s\n", buf);
! 	}
  
! 	fflush(stdout);
! 	setreuid(userid, 0);
! 	rem = rcmd(&rhost, port, user, ruser, buf, 0);
! 	setreuid(0, userid);
! 	if (rem < 0)
! 		return(0);
! 	cp = buf;
! 	if (read(rem, cp, 1) != 1)
! 		lostconn();
! 	if (*cp == 'V') {
! 		do {
! 			if (read(rem, cp, 1) != 1)
! 				lostconn();
! 		} while (*cp++ != '\n' && cp < &buf[BUFSIZ]);
! 		*--cp = '\0';
! 		cp = buf;
! 		n = 0;
! 		while (*cp >= '0' && *cp <= '9')
! 			n = (n * 10) + (*cp++ - '0');
! 		if (*cp == '\0' && n == VERSION)
! 			return(1);
! 		error("connection failed: version numbers don't match (local %d, remote %d)\n", VERSION, n);
! 	} else
! 		error("connection failed: version numbers don't match\n");
! 	closeconn();
! 	return(0);
  }
  
  /*
--- 209,319 ----
  		ruser = user;
  	if (!qflag)
  		printf("updating host %s\n", rhost);
  
!         /* Algorithm
!          * 1) try rsh ..
!          * 2) try rexec with saved password (if it exists)
!          * 3) try rexec and ask for new password
!          */
! 
!         if (norshflag)
!           state = 2;
!         else
!           state = 0;
! 
!         while (state < 6)
!           {
!             static char *password = NULL;
! 
!             switch (state)
!               {
!               case 0:
!                 rem = rsh_connect (rhost, ruser);
!                 mode = "rsh, retrying using rexec ...";
!                 state++;
!                 goto newstate;
! 
!               case 2:
!                 if (password)
!                   {
!                     rem = rexec_connect (rhost, ruser, &password);
!                     mode = "rexec and last password, retrying ...";
!                     state++;
!                   }
!                 else
!                     state = 4;
!                 
!                 goto newstate;
!                 
!               case 4:
!                 password = NULL;
!                 rem = rexec_connect (rhost, ruser, &password);
!                 mode = "rexec.  Giving up.";
!                 state++;
!                 goto newstate;
! 
!               case 1:
!               case 3:
!               case 5:
!                 if (rem < 0)
!                   {
!                     state++;
!                     goto newstate;
!                   }
! 
!                 cp = buf;
!                 if (read(rem, cp, 1) != 1)
!                   {
!                     close (rem);
!                     state++;
!                     goto newstate;
! 
!                   }
! 
!                 if (*cp == 'V')
!                   {
!                     do
!                       {
!                         if (read(rem, cp, 1) != 1)
!                           {
!                             close (rem);
!                             state++;
!                             goto newstate;
!                           }
!                       } while (*cp++ != '\n' && cp < &buf[BUFSIZ]);
! 
!                     *--cp = '\0';
!                     cp = buf;
!                     n = 0;
!                     while (*cp >= '0' && *cp <= '9')
!                       n = (n * 10) + (*cp++ - '0');
! 
!                     if (*cp == '\0' && n == VERSION)
!                       return (1);
! 
!                     error("connection failed: version numbers don't match (local %d, remote %d)\n", VERSION, n);
!                 }
!                 else
!                   error("connection failed using %s\n", mode);
! 
!                 close (rem);
!                 rem = -1;
!                 
!               case 6:
!               default:
!                 state++;
!                 break;
!                 
!               }
!           newstate:
!             ;
!           }
  
!         if (rem < 0)
!           return (0);
! 
!         closeconn();
!         return (0);
  }
  
  /*
*** main.c.orig	Thu Mar 17 17:16:21 1994
--- main.c	Fri Feb  4 17:24:25 1994
***************
*** 40,45 ****
--- 40,46 ----
  int	debug;		/* debugging flag */
  int	nflag;		/* NOP flag, just print commands without executing */
  int	qflag;		/* Quiet. Don't print messages */
+ int	norshflag;      /* Don't use rsh - always use rexec */
  int	options;	/* global options */
  int	iamremote;	/* act as remote server for transfering files */
  
***************
*** 106,111 ****
--- 107,116 ----
  
  			case 'D':
  				debug++;
+ 				break;
+ 
+                         case 'E':
+ 				norshflag++;
  				break;
  
  			case 'c':
*** /dev/null	Thu Mar 17 18:38:05 1994
--- rconn.c	Fri Feb  4 13:46:28 1994
***************
*** 0 ****
--- 1,127 ----
+ 
+ #include "defs.h"
+ #include <sys/types.h>
+ #include <sys/socket.h>
+ #include <netdb.h>
+ #include <netinet/in.h>
+ #include <string.h>
+ #include <stdlib.h>
+ #include <unistd.h>
+ #include <stdio.h>
+ 
+ #ifndef RDIST
+ #define	RDIST "/usr/ucb/rdist"
+ #endif
+ 
+ #ifndef RSH
+ #define RSH "/usr/ucb/rsh"
+ #endif
+ 
+ extern char *getpass();
+      
+ int rexec_connect (rhost, ruser, rpass)
+      char *rhost, *ruser, **rpass;
+ {
+   u_short port;
+   static struct servent *srv_rexec = NULL;
+   struct hostent *hent;
+   char *hostname, prompt[MAXHOSTNAMELEN+32];
+   char rcmd[MAXHOSTNAMELEN];
+   
+   if (!srv_rexec)
+     {
+       srv_rexec = getservbyname ("exec", "tcp");
+       if (!srv_rexec)
+ 	{
+ 	  perror("unknown service 'exec'");
+ 	  exit (2);
+ 	}
+     }
+ 
+   port = srv_rexec->s_port;
+   
+   hent = gethostbyname (rhost);
+ 
+   if (!hent || !(hent->h_name))
+     {
+       perror("unknown host");
+       return (-1);
+     }
+ 
+   hostname = strdup (hent->h_name);
+   
+   if (*rpass == NULL)
+     {
+       strcpy (prompt, "Password for ");
+       strcat (prompt, hostname);
+       strcat (prompt, ": ");
+       *rpass = getpass (prompt);
+     }
+   
+   sprintf (rcmd, "%s -Server%s", RDIST, (qflag) ? " -q" : "");
+   
+   return (rexec(&hostname, port, ruser, *rpass, rcmd, NULL));
+ }
+ 
+ 
+ int rsh_connect (rhost, ruser)
+      char *rhost, *ruser;
+ {     
+   char *rargv[8], rargc = 0;
+   int rem, n, fdes[2];
+ 
+   rargv[rargc++] = RSH;		/* ARGV[0] */
+ 	
+   rargv[rargc++] = "-l";	/* ARGV[1] */
+   rargv[rargc++] = ruser;	/* ARGV[2] */
+ 
+   rargv[rargc++] = rhost;	/* ARGV[3] */
+ 	
+   rargv[rargc++] = RDIST;	/* ARGV[4] */
+   rargv[rargc++] = "-Server";	/* ARGV[5] */
+ 
+   if (qflag)
+     {
+       rargv[rargc++] = "-q";	/* ARGV[6] */
+     }
+ 
+   rargv[rargc] = NULL;		/* ARGV[7] */
+ 	
+   if (socketpair (AF_UNIX, SOCK_STREAM, 0, fdes) < 0)
+     return (-1);
+ 	
+   rem = fdes[0];
+ 	
+   switch (fork())
+     {
+     case 0:			/* Child */
+       {
+ 	int i, err = 0;
+ 	      
+ 	err  = (dup2 (fdes[1], 0) == -1);
+ 	err |= (dup2 (fdes[1], 1) == -1);
+ 	err |= (dup2 (fdes[1], 2) == -1);
+ 
+ 	if (err)
+ 	  exit (1);
+ 	      
+ 	for (i = 3; i < sysconf(_SC_OPEN_MAX); i++)
+ 	  close (i);
+ 
+ 	execv (RSH, rargv);
+ 	exit (2);
+       }
+ 
+     case -1:			/* Failed fork() */
+       close (fdes[0]);
+       close (fdes[1]);
+       return (-1);
+ 
+     default:			/* Parent */
+       close (fdes[1]);
+       break;
+     }
+ 
+   return (rem);
+ }
+